package com.whfc.influx.dao.impl;

import com.alibaba.fastjson.JSON;
import com.whfc.fse.constant.CrawlerMeasurement;
import com.whfc.fse.dto.FseCrawlerDataLogDTO;
import com.whfc.fse.entity.FseCrawlerDataLog;
import com.whfc.influx.dao.FseCrawlerDataLogDao;
import org.apache.commons.lang3.StringUtils;
import org.influxdb.InfluxDB;
import org.influxdb.dto.BatchPoints;
import org.influxdb.dto.Point;
import org.influxdb.dto.Query;
import org.influxdb.impl.InfluxDBMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.util.ObjectUtils;

import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static org.influxdb.querybuilder.BuiltQuery.QueryBuilder.*;

/**
 * @author qzexing
 * @version 1.0
 * @date 2021/11/1 19:02
 */
@Repository
public class FseCrawlerDataLogDaoImpl implements FseCrawlerDataLogDao {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * 数据库名称
     */
    private final String database = CrawlerMeasurement.DATABASE;

    /**
     * 表名
     */
    private final String measurement = CrawlerMeasurement.MEASUREMENT;

    /**
     * 保留策略
     */
    private final String retentionPolicy = CrawlerMeasurement.RETENTION_POLICY;

    /**
     * 时间单位:秒
     */
    private final TimeUnit timeUnit = TimeUnit.SECONDS;

    /**
     * 采样查询结果字段
     */
    private static StringBuilder SAMPLING_COLUMNS = new StringBuilder();

    static {
        SAMPLING_COLUMNS.append("last(mainHookWeight) as mainHookWeight,");
        SAMPLING_COLUMNS.append("last(mainHookHeight) as mainHookHeight,");
        SAMPLING_COLUMNS.append("last(mainHookMoment) as mainHookMoment,");
        SAMPLING_COLUMNS.append("last(viceHookWeight) as viceHookWeight,");
        SAMPLING_COLUMNS.append("last(viceHookHeight) as viceHookHeight,");
        SAMPLING_COLUMNS.append("last(viceHookMoment) as viceHookMoment,");
        SAMPLING_COLUMNS.append("last(range) as range,");
        SAMPLING_COLUMNS.append("last(windSpeed) as windSpeed,");
        SAMPLING_COLUMNS.append("last(turnSpeed) as turnSpeed,");
        SAMPLING_COLUMNS.append("last(turnAngle) as turnAngle,");
        SAMPLING_COLUMNS.append("last(dipAngle) as dipAngle,");
        SAMPLING_COLUMNS.append("last(levelness) as levelness,");
        SAMPLING_COLUMNS.append("last(probeDistances) as probeDistances,");
        SAMPLING_COLUMNS.append("last(probeDistanceWarns) as probeDistanceWarns");
    }

    @Autowired
    private InfluxDB influxDb;

    @Autowired
    private InfluxDBMapper influxDbMapper;


    @Override
    public void insert(FseCrawlerDataLog record) {
        logger.debug("influxdb的insert方法");
        influxDb.setDatabase(database);
        influxDb.setRetentionPolicy(retentionPolicy);
        influxDb.write(this.buildDataPoint(record));
    }

    @Override
    public void batchInsert(List<FseCrawlerDataLog> logList) {
        logger.debug("influxdb的batchInsert方法");
        BatchPoints.Builder batchBuilder = BatchPoints.builder();
        for (FseCrawlerDataLog record : logList) {
            Point point = this.buildDataPoint(record);
            batchBuilder.point(point);
        }
        influxDb.setDatabase(database);
        influxDb.setRetentionPolicy(retentionPolicy);
        influxDb.write(batchBuilder.build());
    }


    @Override
    public List<FseCrawlerDataLogDTO> selectCrawlerDataLogListByCrawlerId(Integer crawlerId, Date startTime, Date endTime) {
        logger.info("influxdb的selectCrawlerDataLogListByCrawlerId方法");
        Query query = select().from(database, measurement)
                .where(eq("crawlerId", crawlerId))
                .andNested()
                .and(gte("time", startTime.toInstant().toString()))
                .and(lte("time", endTime.toInstant().toString()))
                .close()
                .orderBy(asc());
        logger.info(query.getCommand());
        List<FseCrawlerDataLogDTO> list = influxDbMapper.query(query, FseCrawlerDataLogDTO.class);
        this.transformData(list);
        return list;
    }

    @Override
    public List<FseCrawlerDataLogDTO> selectCrawlerDataSamplingByCrawlerId(Integer crawlerId, Date startTime, Date endTime) {
        logger.info("influxdb的selectCrawlerDataSamplingListByCrawlerId方法");
        Query query = select()
                .raw(SAMPLING_COLUMNS.toString())
                .from(database, measurement)
                .where(eq("crawlerId", crawlerId))
                .andNested()
                .and(gte("time", startTime.toInstant().toString()))
                .and(lte("time", endTime.toInstant().toString()))
                .close()
                .groupBy(time(1L, "m"))
                .orderBy(asc());
        logger.info(query.getCommand());
        List<FseCrawlerDataLogDTO> list = influxDbMapper.query(query, FseCrawlerDataLogDTO.class);
        this.transformData(list);
        return list;
    }

    @Override
    public List<FseCrawlerDataLogDTO> selectCrawlerDataSamplingByCrawlerId(Integer crawlerId, Date startTime, Date endTime, String[] columns) {
        logger.info("influxdb的selectCrawlerDataSamplingListByCrawlerId方法");

        String raw = rawSampleData(columns);
        Query query = select()
                .raw(raw)
                .from(database, measurement)
                .where(eq("crawlerId", crawlerId))
                .andNested()
                .and(gte("time", startTime.toInstant().toString()))
                .and(lte("time", endTime.toInstant().toString()))
                .close()
                .groupBy(time(1L, "m"))
                .orderBy(asc());
        logger.info(query.getCommand());
        List<FseCrawlerDataLogDTO> list = influxDbMapper.query(query, FseCrawlerDataLogDTO.class);
        this.transformData(list);
        return list;
    }

    /**
     * 构建数据点
     *
     * @param record 数据
     * @return 数据点
     */
    private Point buildDataPoint(FseCrawlerDataLog record) {
        logger.debug("influxdb的buildDataPoint方法");
        Point.Builder builder = Point
                .measurement(measurement)
                .time(record.getTime().toInstant().getEpochSecond(), timeUnit);
        if (!ObjectUtils.isEmpty(record.getCrawlerId())) {
            builder.tag("crawlerId", String.valueOf(record.getCrawlerId()));
        }
        if (!ObjectUtils.isEmpty(record.getMainHookWeight())) {
            builder.addField("mainHookWeight", record.getMainHookWeight());
        }
        if (!ObjectUtils.isEmpty(record.getMainHookHeight())) {
            builder.addField("mainHookHeight", record.getMainHookHeight());
        }
        if (!ObjectUtils.isEmpty(record.getMainHookMoment())) {
            builder.addField("mainHookMoment", record.getMainHookMoment());
        }
        if (!ObjectUtils.isEmpty(record.getViceHookWeight())) {
            builder.addField("viceHookWeight", record.getViceHookWeight());
        }
        if (!ObjectUtils.isEmpty(record.getViceHookHeight())) {
            builder.addField("viceHookHeight", record.getViceHookHeight());
        }
        if (!ObjectUtils.isEmpty(record.getViceHookMoment())) {
            builder.addField("viceHookMoment", record.getViceHookMoment());
        }
        if (!ObjectUtils.isEmpty(record.getRange())) {
            builder.addField("range", record.getRange());
        }
        if (!ObjectUtils.isEmpty(record.getCrawlerId())) {
            builder.addField("crawlerId", record.getCrawlerId());
        }
        if (!ObjectUtils.isEmpty(record.getWindSpeed())) {
            builder.addField("windSpeed", record.getWindSpeed());
        }
        if (!ObjectUtils.isEmpty(record.getTurnSpeed())) {
            builder.addField("turnSpeed", record.getTurnSpeed());
        }
        if (!ObjectUtils.isEmpty(record.getTurnAngle())) {
            builder.addField("turnAngle", record.getTurnAngle());
        }
        if (!ObjectUtils.isEmpty(record.getDipAngle())) {
            builder.addField("dipAngle", record.getDipAngle());
        }
        if (!ObjectUtils.isEmpty(record.getLevelness())) {
            builder.addField("levelness", record.getLevelness());
        }
        if (!ObjectUtils.isEmpty(record.getConditions())) {
            builder.addField("conditions", record.getConditions());
        }
        if (!ObjectUtils.isEmpty(record.getTotalOverloadCnt())) {
            builder.addField("totalOverloadCnt", record.getTotalOverloadCnt());
        }
        if (!ObjectUtils.isEmpty(record.getTotalWorkCnt())) {
            builder.addField("totalWindWorkCnt", record.getTotalWorkCnt());
        }
        if (!ObjectUtils.isEmpty(record.getTotalWorkTimes())) {
            builder.addField("totalWorkTimes", record.getTotalWorkTimes());
        }
        if (!ObjectUtils.isEmpty(record.getTotalWorkCnt())) {
            builder.addField("totalWorkCnt", record.getTotalWorkCnt());
        }
        if (!ObjectUtils.isEmpty(record.getWorkStartTime())) {
            builder.addField("workStartTime", record.getWorkStartTime().getTime());
        }
        if (!ObjectUtils.isEmpty(record.getWorkEndTime())) {
            builder.addField("workEndTime", record.getWorkEndTime().getTime());
        }
        if (!ObjectUtils.isEmpty(record.getTotalMainCnt())) {
            builder.addField("totalMainCnt", record.getTotalMainCnt());
        }
        if (!ObjectUtils.isEmpty(record.getTotalViceCnt())) {
            builder.addField("totalViceCnt", record.getTotalViceCnt());
        }
        if (!ObjectUtils.isEmpty(record.getTotalTurnCnt())) {
            builder.addField("totalTurnCnt", record.getTotalTurnCnt());
        }
        if (!ObjectUtils.isEmpty(record.getState())) {
            builder.addField("state", record.getState());
        }
        if (!ObjectUtils.isEmpty(record.getAction())) {
            builder.addField("action", record.getAction());
        }
        if (!ObjectUtils.isEmpty(record.getTrip())) {
            builder.addField("trip", record.getTrip());
        }
        if (!ObjectUtils.isEmpty(record.getTurnWarn())) {
            builder.addField("turnWarn", record.getTurnWarn());
        }
        if (!ObjectUtils.isEmpty(record.getMainHookHeightWarn())) {
            builder.addField("mainHookHeightWarn", record.getMainHookHeightWarn());
        }
        if (!ObjectUtils.isEmpty(record.getMainHookWeightWarn())) {
            builder.addField("mainHookWeightWarn", record.getMainHookWeightWarn());
        }
        if (!ObjectUtils.isEmpty(record.getViceHookHeightWarn())) {
            builder.addField("viceHookHeightWarn", record.getViceHookHeightWarn());
        }
        if (!ObjectUtils.isEmpty(record.getViceHookWeightWarn())) {
            builder.addField("viceHookWeightWarn", record.getViceHookWeightWarn());
        }
        if (!ObjectUtils.isEmpty(record.getDipAngleWarn())) {
            builder.addField("dipAngleWarn", record.getDipAngleWarn());
        }
        if (!ObjectUtils.isEmpty(record.getWindSpeedWarn())) {
            builder.addField("windSpeedWarn", record.getWindSpeedWarn());
        }
        if (!ObjectUtils.isEmpty(record.getAbnormalOpWarn())) {
            builder.addField("abnormalOpWarn", record.getAbnormalOpWarn());
        }
        if (!ObjectUtils.isEmpty(record.getTurnLeft())) {
            builder.addField("turnLeft", record.getTurnLeft());
        }
        if (!ObjectUtils.isEmpty(record.getTurnRight())) {
            builder.addField("turnRight", record.getTurnRight());
        }
        if (!ObjectUtils.isEmpty(record.getMainHookUp())) {
            builder.addField("mainHookUp", record.getMainHookUp());
        }
        if (!ObjectUtils.isEmpty(record.getMainHookDown())) {
            builder.addField("mainHookDown", record.getMainHookDown());
        }
        if (!ObjectUtils.isEmpty(record.getViceHookUp())) {
            builder.addField("viceHookUp", record.getViceHookDown());
        }
        if (!ObjectUtils.isEmpty(record.getViceHookDown())) {
            builder.addField("viceHookDown", record.getViceHookDown());
        }
        if (!ObjectUtils.isEmpty(record.getMainHookBrake())) {
            builder.addField("mainHookBrake", record.getMainHookBrake());
        }
        if (!ObjectUtils.isEmpty(record.getViceHookBrake())) {
            builder.addField("viceHookBrake", record.getViceHookBrake());
        }
        if (!ObjectUtils.isEmpty(record.getDipAngleLimit())) {
            builder.addField("dipAngleLimit", record.getDipAngleLimit());
        }
        if (!ObjectUtils.isEmpty(record.getDoorLimit())) {
            builder.addField("doorLimit", record.getDoorLimit());
        }
        if (!ObjectUtils.isEmpty(record.getMainHookHeightUpperLimit())) {
            builder.addField("mainHookHeightUpperLimit", record.getMainHookHeightUpperLimit());
        }
        if (!ObjectUtils.isEmpty(record.getMainHookHeightLowerLimit())) {
            builder.addField("mainHookHeightLowerLimit", record.getMainHookHeightLowerLimit());
        }
        if (!ObjectUtils.isEmpty(record.getViceHookHeightUpperLimit())) {
            builder.addField("viceHookHeightUpperLimit", record.getViceHookHeightUpperLimit());
        }
        if (!ObjectUtils.isEmpty(record.getViceHookHeightLowerLimit())) {
            builder.addField("viceHookHeightLowerLimit", record.getViceHookHeightLowerLimit());
        }
        if (!ObjectUtils.isEmpty(record.getMinHeight())) {
            builder.addField("minHeight", record.getMinHeight());
        }
        if (!ObjectUtils.isEmpty(record.getMaxHeight())) {
            builder.addField("maxHeight", record.getMaxHeight());
        }
        if (!ObjectUtils.isEmpty(record.getProbeDistances())) {
            builder.addField("probeDistances", record.getProbeDistances());
        }
        if (!ObjectUtils.isEmpty(record.getProbeDistanceWarns())) {
            builder.addField("probeDistanceWarns", record.getProbeDistanceWarns());
        }
        builder.addField("serverTime", System.currentTimeMillis() / 1000);
        return builder.build();
    }

    /**
     * 数据处理
     *
     * @param list 数据
     */
    private void transformData(List<FseCrawlerDataLogDTO> list) {
        for (FseCrawlerDataLogDTO dto : list) {
            //处理时间
            dto.setTime(Date.from(dto.getDeviceTime()));
            if (!ObjectUtils.isEmpty(dto.getServerTime())) {
                dto.setCreateTime(new Date(dto.getServerTime() * 1000));
            }
            //处理防碰撞信息
            String probeDistances = dto.getProbeDistances();
            if (StringUtils.isNotBlank(probeDistances)) {
                List<Double> probeDistanceList = JSON.parseArray(probeDistances, Double.class);
                dto.setProbeDistanceList(probeDistanceList);
            }
            dto.setProbeDistances(null);
            //处理防撞报警
            String probeDistanceWarns = dto.getProbeDistanceWarns();
            if (StringUtils.isNotBlank(probeDistanceWarns)) {
                List<Integer> warnList = JSON.parseArray(probeDistanceWarns, Integer.class);
                int probeDistanceWarn = 0;
                for (Integer i : warnList) {
                    probeDistanceWarn += i;
                }
                if (probeDistanceWarn > 0) {
                    probeDistanceWarn = 1;
                } else {
                    probeDistanceWarn = 0;
                }
                dto.setProbeDistanceWarn(probeDistanceWarn);
            }
            dto.setProbeDistanceWarns(null);
        }
    }

    /**
     * 获取采样数据
     *
     * @param columns
     * @return
     */
    private String rawSampleData(String[] columns) {
        String raw = null;
        if (columns == null || columns.length == 0) {
            raw = SAMPLING_COLUMNS.toString();
        } else {
            StringBuilder builder = new StringBuilder();
            for (String column : columns) {
                builder.append("last(").append(column).append(") as ").append(column).append(",");
            }
            raw = builder.substring(0, builder.length() - 1);
        }
        return raw;
    }
}
